﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BaseFileUseCase.cs" company="">
//   
// </copyright>
// <summary>
//   The base file use case.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Libium.UseCases
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;

    using Libium.Core;
    using Libium.Utilities;

    /// <summary>
    ///   The base file use case.
    /// </summary>
    public abstract class BaseFileUseCase : CompoundUseCase, IFileUseCase
    {
        #region Fields

        private object _syncObject = new object();
		public static ISynchronizeInvoke SynchronizingObject;

        /// <summary>
        ///   The _edit file path.
        /// </summary>
        protected string EditFilePath { get; set; }

        /// <summary>
        ///   The _edit id.
        /// </summary>
        private Guid _editId;

        /// <summary>
        ///   The _file size.
        /// </summary>
        private long _fileSize;

        /// <summary>
        ///   The _file watcher.
        /// </summary>
        private FileSystemWatcher _fileWatcher;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseFileUseCase"/> class.
        /// </summary>
        /// <param name="parent">
        /// The parent. 
        /// </param>
        /// <param name="service">
        /// The service. 
        /// </param>
        protected BaseFileUseCase(ICompoundUseCase parent, IUseCaseInstanceService service)
            : base(parent, service)
        {
            FileSizeDigitsAfterZero = 0;
            ContentName = "File";
            BrowseCommand = AddCommand(StandardCommandDefinitions.BrowseCommand, OnLoadCommand);
            SaveAsCommand = AddCommand(StandardCommandDefinitions.SaveAsCommand, OnSaveAsCommand);
            EditCommand = AddCommand(StandardCommandDefinitions.EditCommand, OnOpenCommand);
				//Currently this option is not usefull. But maybe usefull in future.
            //RefreshCommand = AddCommand(StandardCommandDefinitions.RefreshCommand, OnRefreshCommand);
            CopyCommand = AddCommand(StandardCommandDefinitions.CopyCommand, OnCopyCommand);
            CutCommand = AddCommand(StandardCommandDefinitions.CutCommand, OnCutCommand);
            PasteCommand = AddCommand(StandardCommandDefinitions.PasteCommand, OnPasteCommand);
            ClearCommand = AddCommand(StandardCommandDefinitions.ClearCommand, OnClearCommand);
        }

        #endregion

        #region Public Events

        /// <summary>
        ///   The file changed.
        /// </summary>
        public event Action<IFileUseCase> FileChanged;

        #endregion

        #region Public Properties

        /// <summary>
        ///   Gets the clear command.
        /// </summary>
        public ICommand ClearCommand { get; private set; }

        /// <summary>
        ///   Gets or sets the content name.
        /// </summary>
        public string ContentName { get; set; }

        /// <summary>
        ///   Gets the copy command.
        /// </summary>
        public ICommand CopyCommand { get; private set; }

        /// <summary>
        ///   Gets the cut command.
        /// </summary>
        public ICommand CutCommand { get; private set; }

        public int FileSizeDigitsAfterZero { get; set; }

        /// <summary>
        ///   Gets the details text.
        /// </summary>
        public virtual string DetailsText
        {
            get
            {
                if (string.IsNullOrEmpty(FilePath))
                {
                    return "No " + ContentName.ToLower();
                }

                var fileSize = FileUtility.GetFileSizeString(FilePath, FileSizeDigitsAfterZero);
                return Path.GetFileName(FilePath) + ", " + fileSize;
            }
        }

        /// <summary>
        ///   Gets a value indicating whether empty.
        /// </summary>
        public override bool Empty
        {
            get
            {
                return string.IsNullOrEmpty(FilePath);
            }
        }

        protected string FilePathInternal { get; set; }

        /// <summary>
        ///   Gets or sets the file path.
        /// </summary>
        public virtual string FilePath
        {
            get
            {
                return FilePathInternal;
            }

            set
            {
                if (FilePathInternal == value)
                {
                    return;
                }

                FilePathInternal = value;
                OnFileChanged();
            }
        }

        /// <summary>
        ///   Gets the load command.
        /// </summary>
        public ICommand BrowseCommand { get; private set; }

        /// <summary>
        ///   Gets or sets the mime types.
        /// </summary>
        public MimeType[] MimeTypes { get; set; }

        /// <summary>
        ///   Gets the open command.
        /// </summary>
        public ICommand EditCommand { get; private set; }

        /// <summary>
        ///   Gets the paste command.
        /// </summary>
        public ICommand PasteCommand { get; private set; }

        /// <summary>
        ///   Gets the refresh command.
        /// </summary>
        public ICommand RefreshCommand { get; private set; }

        /// <summary>
        ///   Gets the save as command.
        /// </summary>
        public ICommand SaveAsCommand { get; private set; }

        /// <summary>
        ///   Gets or sets a value indicating whether show only extension.
        /// </summary>
        public bool ShowOnlyExtension { get; set; }

        /// <summary>
        ///   Gets the text.
        /// </summary>
        public virtual string Text
        {
            get
            {
                if (string.IsNullOrEmpty(FilePath))
                {
                    return "No " + ContentName.ToLower();
                }

                var fileSize = FileUtility.GetFileSizeString(FilePath, FileSizeDigitsAfterZero);

                if (ShowOnlyExtension)
                {
                    return Path.GetExtension(FilePath).Substring(1).ToUpper() + "\r\n" + fileSize;
                }

					 return Path.GetFileName(FilePath) + "\r\n" + fileSize;
            }
        }

		  private void UpdateFile(string source)
		  {
              lock (_syncObject)              
              {
                  var file = new LocalFile(source);
                  if (!IsValidFile(file, false))
                  {
                      return;
                  }
                  var name = Path.GetFileName(source);
                  string localFilePath = FileUtility.GetTemporaryFilePath(name);

                  try
                  {
                      File.Copy(source, localFilePath, true);
                      UpdateFileAfterEdit(localFilePath);
                  }
                  catch (UnauthorizedAccessException exception)
                  {
                  }
                  catch (IOException exception)
                  {
                  }
              }
		  }

          //TODO Refactor
          protected virtual void UpdateFileAfterEdit(string filePath)
          {
              if (File.Exists(filePath))
              {
                  FilePath = filePath;
              }
          }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        ///   The make empty.
        /// </summary>
        public override void MakeEmpty()
        {
            base.MakeEmpty();
            FilePath = null;
        }

        #endregion

        #region Methods

        /// <summary>
        ///   The build copy options.
        /// </summary>
        /// <returns> The <see cref="CopyOptions" /> . </returns>
        protected override CopyOptions BuildCopyOptions()
        {
            var options = base.BuildCopyOptions();
            var can = !string.IsNullOrEmpty(FilePath);
            options.CanCopy |= can;
            options.CanMove |= can;
            return options;
        }

        /// <summary>
        /// The build paste options.
        /// </summary>
        /// <param name="data">
        /// The data. 
        /// </param>
        /// <returns>
        /// The <see cref="PasteOptions"/> . 
        /// </returns>
        protected override PasteOptions BuildPasteOptions(ICopyData data)
        {
            var options = base.BuildPasteOptions(data);
            var can = data.IsFileExist(MimeTypes);
            options.CanCopy |= can;
            options.CanMove |= can;
            return options;
        }

        /// <summary>
        /// The do finish copy.
        /// </summary>
        /// <param name="action">
        /// The action. 
        /// </param>
        protected override void DoFinishCopy(CopyAction action)
        {
            base.DoFinishCopy(action);
            if (action == CopyAction.Move)
            {
                FilePath = null;
            }
        }

        /// <summary>
        /// The do paste.
        /// </summary>
        /// <param name="data">
        /// The data. 
        /// </param>
        /// <param name="action">
        /// The action. 
        /// </param>
        protected override void DoPaste(ICopyData data, CopyAction action)
        {
            var file = data.GetFile(NotExistsAction.ReturnNull);
            if (file != null)
            {
                if (IsValidFile(file))
                {
                    var copyFile = LocalFile.CreateTemporaryFile(file.GetDescription().Extension);
                    file.CopyTo(copyFile);
                    FilePath = copyFile.GetPath();
                }

                return;
            }

            base.DoPaste(data, action);
        }

        /// <summary>
        ///   The do start copy.
        /// </summary>
        /// <returns> The <see cref="ICopyData" /> . </returns>
        protected override ICopyData DoStartCopy()
        {
            var data = base.DoStartCopy();
            data.SetFile(new LocalFile(FilePath));
            return data;
        }

        /// <summary>
        /// The is valid file.
        /// </summary>
        /// <param name="file">
        /// The file. 
        /// </param>
        /// <returns>
        /// The <see cref="bool"/> . 
        /// </returns>
        protected virtual bool IsValidFile(IFile file, bool showMessage = true)
        {
            var mimeType = file.GetDescription().MimeType;
            if (MimeTypes.All(mt => mt != mimeType))
            {
                if (showMessage)
                {
                    ErrorWithOk(
                        "File mime type <" + mimeType + "> is invalid. Valid types are: "
                        + Core.MimeTypes.ToString(MimeTypes));
                }
                return false;
            }

            return true;
        }

        /// <summary>
        /// The on clear command.
        /// </summary>
        /// <param name="call">
        /// The call. 
        /// </param>
        protected virtual void OnClearCommand(CommandCall call)
        {
            FilePath = null;
        }

        /// <summary>
        ///   The on file changed.
        /// </summary>
        protected virtual void OnFileChanged()
        {
            Modified = true;

            if (FileChanged != null)
            {
                FileChanged(this);
            }
        }

        /// <summary>
        /// The on load command.
        /// </summary>
        /// <param name="call">
        /// The call. 
        /// </param>
        protected virtual void OnLoadCommand(CommandCall call)
        {
            var filter = new FileFilter { Name = "Files", MimeTypes = MimeTypes };
            var openFilePath = ExecuteOpenFile(filter);
            if (openFilePath == null)
            {
                return;
            }

            FilePath = FileUtility.CopyToTemporaryFile(openFilePath);
        }

        /// <summary>
        /// The on open command.
        /// </summary>
        /// <param name="call">
        /// The call. 
        /// </param>
        protected virtual void OnOpenCommand(CommandCall call)
        {
			  StartEdit(FilePath);
        }

		  /// <summary>
		  /// The intermediate on-open command.
		  /// </summary>
		  /// <param name="call">
		  /// The call. 
		  /// </param>
		  public void StartEdit(string filePath)
		  {
			  if (filePath == null)
			  {
				  return;
			  }

			  // Add menu to choose handler
			  _editId = Guid.NewGuid();

			  var directoryPath = Path.Combine(Service.TemporaryFilePath, _editId.ToString());
			  Directory.CreateDirectory(directoryPath);

			  EditFilePath = Path.Combine(directoryPath, Path.GetFileName(filePath));
			  File.Copy(filePath, EditFilePath, true);

			  if (_fileWatcher != null)
			  {
				   _fileWatcher.Dispose();
			  }

			  _fileWatcher = new FileSystemWatcher(directoryPath, "*.*");
			  _fileWatcher.SynchronizingObject = SynchronizingObject;

			  // TODO Add refresh enable/disable
              _fileWatcher.Created += (s, e) => UpdateFile(e.FullPath);
              _fileWatcher.Changed += (s, e) => UpdateFile(e.FullPath);
              _fileWatcher.Renamed += (s, e) => UpdateFile(e.FullPath);
			  _fileWatcher.EnableRaisingEvents = true;

			  try
			  {
				  Process.Start(EditFilePath);
			  }
			  catch (Win32Exception ex)
			  {
				  if (!ex.Message.StartsWith("No application"))
				  {
					  throw;
				  }

				  Process.Start("rundll32.exe", "shell32.dll, OpenAs_RunDLL " + EditFilePath);
			  }
		  }

        /// <summary>
        /// The on refresh command.
        /// </summary>
        /// <param name="call">
        /// The call. 
        /// </param>
        protected virtual void OnRefreshCommand(CommandCall call)
        {
            if (EditFilePath == null)
            {
                return;
            }

			  if(EditFilePath != FilePath)
            File.Copy(EditFilePath, FilePath, true);
            OnFileChanged();
        }

        /// <summary>
        /// The on save as command.
        /// </summary>
        /// <param name="call">
        /// The call. 
        /// </param>
        protected virtual void OnSaveAsCommand(CommandCall call)
        {
            if (FilePath == null)
            {
                return;
            }

            var fileName = Path.GetFileName(FilePath);
            var mimeType = Core.MimeTypes.GetByExtension(PathUtility.GetExtensionWithoutDot(fileName));
            var filter = new FileFilter { Name = "Files", MimeTypes = new [] {mimeType}};
            var saveFilePath = ExecuteSaveFile(fileName, filter);
            if (saveFilePath == null)
            {
                return;
            }

            File.Copy(FilePath, saveFilePath, true);
        }

        #endregion

		  protected static bool IsFileReady(String sFilename)
		  {
			  // If the file can be opened for exclusive access it means that the file
			  // is no longer locked by another process.
			  try
			  {
				  using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
				  {
					  if (inputStream.Length > 0)
					  {
						  return true;
					  }
					  else
					  {
						  return false;
					  }

				  }
			  }
			  catch (Exception)
			  {
				  return false;
			  }
		  }

        // var task = ExecuteAsyncTask("Pasting data", data.GetFileAsync(copyResult));
        // task.ContinueWith((Task<IFile> t) =>
        // 	{
        // 		var file = t.Result;
        // 		if (file == null)
        // 		{
        // 			ErrorWithOk("There is no files in the pasted data");
        // 		}
        // 		else
        // 		{
        // 			if (!IsValidFileExtension(file.GetDescription().Extension))
        // 			{
        // 				ErrorWithOk("Pasted file extension '" + file.GetDescription().Extension + "' is not allowed. Allowed extensions are: ");
        // 			}
        // 			else
        // 			{
        // 				FilePath = file.GetPath();
        // 			}
        // 		}
        // 	});
    }
}